{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Using Interrupts and asyncio for Buttons and Switches\n",
    "\n",
    "This notebook provides a simple example for using asyncio I/O to interact asynchronously with multiple input devices. A task is created for each input device and coroutines used to process the results. To demonstrate, we recreate the flashing LEDs example in the getting started notebook but using interrupts to avoid polling the GPIO devices. The aim is have holding a button result in the corresponding LED flashing."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Initialising the Enviroment\n",
    "First we import an instantiate all required classes to interact with the buttons, switches and LED and ensure the base overlay is loaded."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "from pynq import PL\n",
    "from pynq.overlays.base import BaseOverlay\n",
    "\n",
    "base = BaseOverlay(\"base.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Define the flash LED task\n",
    "Next step is to create a task that waits for the button to be pressed and flash the LED until the button is released. The `while True` loop ensures that the coroutine keeps running until cancelled so that multiple presses of the same button can be handled.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "import asyncio\n",
    "\n",
    "@asyncio.coroutine\n",
    "def flash_led(num):\n",
    "    while True:\n",
    "        yield from base.buttons[num].wait_for_value_async(1)\n",
    "        while base.buttons[num].read():\n",
    "            base.leds[num].toggle()\n",
    "            yield from asyncio.sleep(0.1)\n",
    "        base.leds[num].off()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Create the task\n",
    "As there are four buttons we want to check, we create four tasks. The function `asyncio.ensure_future` is used to convert the coroutine to a task and schedule it in the event loop. The tasks are stored in an array so they can be referred to later when we want to cancel them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "tasks = [asyncio.ensure_future(flash_led(i)) for i in range(4)]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Monitoring the CPU Usage\n",
    "\n",
    "One of the advantages of interrupt-based I/O is to minimised CPU usage while waiting for events. To see how CPU usages is impacted by the flashing LED tasks we create another task that prints out the current CPU utilisation every 3 seconds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "import psutil\n",
    "\n",
    "@asyncio.coroutine\n",
    "def print_cpu_usage():\n",
    "    # Calculate the CPU utilisation by the amount of idle time\n",
    "    # each CPU has had in three second intervals\n",
    "    last_idle = [c.idle for c in psutil.cpu_times(percpu=True)]\n",
    "    while True:\n",
    "        yield from asyncio.sleep(3)\n",
    "        next_idle = [c.idle for c in psutil.cpu_times(percpu=True)]\n",
    "        usage = [(1-(c2-c1)/3) * 100 for c1,c2 in zip(last_idle, next_idle)]\n",
    "        print(\"CPU Usage: {0:3.2f}%, {1:3.2f}%\".format(*usage))\n",
    "        last_idle = next_idle\n",
    "\n",
    "tasks.append(asyncio.ensure_future(print_cpu_usage()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Run the event loop\n",
    "All of the blocking wait_for commands will run the event loop until the condition is met. All that is needed is to call the blocking `wait_for_level` method on the switch we are using as the termination condition. \n",
    "\n",
    "While waiting for switch 0 to get high, users can press any push button on the board to flash the corresponding LED. While this loop is running, try opening a terminal and running `top` to see that python is consuming no CPU cycles while waiting for peripherals. \n",
    "\n",
    "As this code runs until the switch 0 is high, make sure it is low before running the example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU Usage: 0.67%, 11.67%\n",
      "CPU Usage: 0.00%, 0.33%\n",
      "CPU Usage: 0.00%, 0.33%\n",
      "CPU Usage: 0.00%, 0.33%\n",
      "CPU Usage: 0.00%, 0.33%\n",
      "CPU Usage: 0.00%, 0.33%\n"
     ]
    }
   ],
   "source": [
    "if base.switches[0].read():\n",
    "    print(\"Please set switch 0 low before running\")\n",
    "else:\n",
    "    base.switches[0].wait_for_value(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Clean up\n",
    "Even though the event loop has stopped running, the tasks are still active and will run again when the event loop is next used. To avoid this, the tasks should be cancelled when they are no longer needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[True, True, True, True, True]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[t.cancel() for t in tasks]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "Now if we re-run the event loop, nothing will happen when we press the buttons. The process will block until the switch is set back down to the low position."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [],
   "source": [
    "base.switches[0].wait_for_value(0)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
